using MathNet.Numerics;
using MeasureAbstract.IMotionDevice;
using MeasureAbstract.IVectorNetworkAnalyzer;
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Metadata.Ecma335;
using System.Threading;
using System.Windows.Documents;

namespace R_SZVA50矢量网络分析仪
{
    public class VectorNetworkAnalyzer : IVectorNetworkAnalyzer
    {
        public VectorNetworkAnalyzer() 
        {
            Config = new VectorNetworkAnalyzerConfig();
            Config.CommunicationList = new ObservableCollection<CommunicationMode>()
            {
                new CommunicationMode() {Name="TCP",Selected=true,Support=true },
                new CommunicationMode() {Name="GPIB",Selected=false,Support=true}
            };
        }
        public override string Name => "矢量网络分析仪";

        public override string Vender => "R&S";

        public override string Model => "ZVA50";

        public override string Description => "罗德与施瓦茨的ZVA50矢量网络分析仪";

        public override int PortCount => 4;

        public override ObservableCollection<string> DataFormatList => new ObservableCollection<string>() { "对数幅度", "相位", "线性幅度", "实部", "虚部", "驻波比", "群延时" };

        public override ObservableCollection<string> Unit => new ObservableCollection<string>() { "Hz", "KHz", "MHz", "GHz" };

        public override string SelectedTriggerInPort
        {
            get { return _SelectedTriggerInPort; }
            set
            {
                SetProperty(ref _SelectedTriggerInPort, value);
            }
        }

        private string _SelectedTriggerInPort="In1";

        public override ObservableCollection<string> TriggerInPorts => new ObservableCollection<string>() {"In1","In2" };

        public override string SelectedTriggerInSignal
        {
            get { return _SelectedTriggerInSignal; }
            set
            {
                SetProperty(ref _SelectedTriggerInSignal, value);
            }
        }
        public string _SelectedTriggerInSignal = "上升沿";
        public override ObservableCollection<string> TriggerInSignals => new ObservableCollection<string>() { "上升沿","下降沿","高电平","低电平"};

        public override string SelectedTriggerInAction
        {
            get { return _SelectedTriggerInAction; }
            set
            {
                SetProperty(ref _SelectedTriggerInAction, value);
            }
        }
        public string _SelectedTriggerInAction = "扫描一个点";

        public override ObservableCollection<string> TriggerInActions => new ObservableCollection<string>() { "扫描一个点","扫描整个通道"};

        public override string SelectedTriggerOutPort
        {
            get { return _SelectedTriggerOutPort; }
            set
            {
                SetProperty(ref _SelectedTriggerOutPort, value);
            }
        }
        public string _SelectedTriggerOutPort = "Out1";

        public override ObservableCollection<string> TriggerOutPorts => new ObservableCollection<string>() { "Out1", "Out2" };

        public override string SelectedTriggerOutSignal
        {
            get { return _SelectedTriggerOutSignal; }
            set
            {
                SetProperty(ref _SelectedTriggerOutSignal, value);
            }
        }
        public string _SelectedTriggerOutSignal = "下降沿";

        public override ObservableCollection<string> TriggerOutSignals => new ObservableCollection<string>() { "上升沿", "下降沿", "高电平", "低电平" };

        public override string SelectedTriggerOutTime
        {
            get { return _SelectedTriggerOutTime; }
            set
            {
                SetProperty(ref _SelectedTriggerOutTime, value);
            }
        }
        public string _SelectedTriggerOutTime = "扫描一个点后触发";

        public override ObservableCollection<string> TriggerOutTimes => new ObservableCollection<string>() { "扫描一个点后触发", "扫描整个通道后触发" };


        public string _SelectedTriggerInSignalWidth = "10";
        public override string SelectedTriggerInSignalWidth
        {
            get { return _SelectedTriggerInSignalWidth; }
            set
            {
                SetProperty(ref _SelectedTriggerInSignalWidth, value);
            }
        }

        public string _Dwell = "20000";
        public override string DWell
        {
            get { return _Dwell; }
            set
            {
                SetProperty(ref _Dwell, value);
            }
        }

        public string _SelectedTriggerOutSignalWidth = "10";
        public override string SelectedTriggerOutSignalWidth
        {
            get { return _SelectedTriggerOutSignalWidth; }
            set
            {
                SetProperty(ref _SelectedTriggerOutSignalWidth, value);
            }
        }

        int sens = 0;
        int vi = 0;
        int ret = 0;
        int CmdDelay_ms = 200;
        string LastSend = "";
        private void Write(string Data)
        {
            if (!Data.Contains("*OPC"))
            {
                LastSend = Data;

            }
            ret = Visa32.viPrintf(vi, Data);
            if (ret != Visa32.VI_SUCCESS)
            {
                throw new Exception($"向{Vender}_{Model}发送指令失败{ret}");
            }
        }

        private void Read(byte[] Data)
        {
            Visa32.viSetAttribute(vi, Visa32.VI_ATTR_TERMCHAR_EN, Visa32.VI_TRUE);
            ret = Visa32.viScanf(vi, "%t", Data);
            if (ret != Visa32.VI_SUCCESS)
            {
                throw new Exception($"从{Vender}_{Model}读取数据失败{ret}\n{LastSend}");
            }
        }

        public override void Connect()
        {
            if (ConnectState)
            {
                return;
            }
            string SelectedCommunication = "";
            foreach (var temp in Config.CommunicationList)
            {
                if (temp.Selected)
                {
                    SelectedCommunication = temp.Name;
                }
            }
            string ResourceName = "";
            int TimeOut_ms = 10000;
            switch (SelectedCommunication.ToUpper())
            {
                case "TCP":
                    ResourceName = $"TCPIP0::{Config.DestIP}::{Config.DestPort}::SOCKET"; break;
                case "GPIB":
                    ResourceName = $"GPIB0::{Config.GpibAddr}::INSTR"; break;
                case "USB":
                    ResourceName = $"USB0::0x1234::0x5678::0::INSTR"; break;
                default: throw new Exception($"不支持的通信方式{SelectedCommunication}");
            }
            Visa32.viOpenDefaultRM(out sens);
            int ret = Visa32.viOpen(sens, ResourceName, Visa32.VI_NO_LOCK, TimeOut_ms, out vi);
            if (ret != 0)
            {
                ConnectState = false;
                throw (new Exception($"{Vender}_{Model}打开失败{ret}"));
            }
            //发送一条信息，确定已经连接
            Write("*IDN?\n");
            byte[] D = new byte[1000];
            try
            {
                Read(D);
                if (!System.Text.Encoding.Default.GetString(D).Contains("Rohde&Schwarz,ZVA50-4Port,1145111052100251,4.00"))
                {
                    throw new Exception($"{System.Text.Encoding.GetEncoding("GBK").GetString(D).Replace("\0", "").Replace("\n", "")}");
                }
            }
            catch (Exception e)
            {
                throw new Exception($"连接矢网失败!\n{e.Message}");
            }
            string Message = System.Text.Encoding.Default.GetString(D);
            ConnectState = true;
        }
        
        public override void DisConnect()
        {

            if (!ConnectState)
            {
                return;
            }
            //OutPut(false);
            //Wait(200);
            int ret = Visa32.viClose(vi);
            if (ret != Visa32.VI_SUCCESS)
            {
                throw new Exception($"{Name}_{Model}断开失败{ret}!");
            }
            ConnectState = false;
        }

        public override void ReSet()
        {
            MalfunctionState = false;
            //重置仪器
            Write("*RST\n");
            Wait(2000);
            //清空状态
            Write("*CLS\n");
            Wait(500);
            //关闭连续扫
            //Write("INITiate:CONTinuous OFF\n");
            Wait(200);
            //删除测量
            Write("CALC:PAR:DEL:ALL\n");
            Wait(200);
            //关射频
            Write("OUTP OFF\n");
            Wait(200);
        }

        public override void OutPut(bool Open)
        {
            if(Open)
            {
                string cmd = $"OUTP ON\n";
                Write(cmd);
                Wait(500);
                //读取仪器状态确认
                Write("OUTPut?\n");
                byte[] Temp=new byte[128];
                try
                {
                    Read(Temp);
                    string Msg = System.Text.Encoding.Default.GetString(Temp).Replace("\0", "").Replace("\n", "");
                    if (Msg == "1")
                    {
                        RadioFrequencyState = true;
                    }
                    else
                    {
                        RadioFrequencyState = false;
                    }
                }
                catch (Exception)
                {
                    throw new Exception("无法确认仪器射频开关状态，请检查!");
                }
                
            }
            else
            {
                string cmd = $"OUTP OFF\n";
                Write(cmd);
                Wait(2000);
                //读取仪器状态确认
                Write("OUTPut?\n");
                byte[] Temp = new byte[128];
                try
                {
                    Read(Temp);
                    string Msg = System.Text.Encoding.Default.GetString(Temp).Replace("\0", "").Replace("\n", "");
                    if (Msg == "1")
                    {
                        RadioFrequencyState = true;
                    }
                    else
                    {
                        RadioFrequencyState = false;
                    }
                }
                catch (Exception e)
                {

                    throw new Exception("无法确认仪器射频开关状态，请检查!");
                }
                
            }
        }

        public override void MeasureOnce(int ChannelNumber = 1)
        {
            //if (ChannelNumber == int.MaxValue)
            //{
            //    //测量所有通道

            //    Write($"INITiate{ChannelNumber}:SCOPe ALL\n");
            //}
            //else
            //{
            //    //测量指定通道
            //    Write($"INITiate{ChannelNumber}:SCOPe SINGle\n");
            //}
            //Write($"INITiate{ChannelNumber}:IMMediate\n");

            //Wait(100);
        }

        public override void ReadData(int ChannelNumber, string MeasureName, out double[] Data)
        {
            string VNAMeasureName = GetVNAMeasureName(MeasureName, DataFormatList);
            SelectMeasurement(ChannelNumber, VNAMeasureName);
            byte[] Temp = new byte[102400];
            string Cmd = $"CALCulate{ChannelNumber}:DATA? FDAT\n";
            Write(Cmd);
            Read(Temp);
            string[] Msg = System.Text.Encoding.Default.GetString(Temp).Replace("\0", "").Replace("\n", "").Split(",");
            if (Msg.Length != FrequencyCount)
            {
                throw new Exception("读取到的数据数量与频点数量不一致!");
            }
            Data = new double[FrequencyCount];
            for (int i = 0; i < Msg.Length; i++)
            {
                Data[i] = Convert.ToDouble(Msg[i]);
            }
        }

        public override int CreateMeasurement(int ChannelNumber, string MeasurementName, string MeasuredParameters)
        {
            string cmd = $"CALCulate{ChannelNumber}:PARameter:DEFine 'Trc{MeasurementName}',{MeasuredParameters}\n;*WAI\n";
            Write(cmd);
            return 0;
        }

        public override int OpenWindow(int WindowNumber)
        {
            string cmd = $"DISPlay:WINDow{WindowNumber}:STATE ON\n;*WAI\n";
            Write(cmd);
            return 0;
        }

        public override int DisplayMeasurement(int WindowNumber, int TraceNumber, string MeasurementName)
        {
            string cmd = $"DISPlay:WINDow{WindowNumber}:TRACe{TraceNumber}:FEED 'Trc{MeasurementName}'\n;*WAI\n";
            Write(cmd);
            return 0;
        }

        public override int DeleteMeasurement(int WindowNumber, int TraceNumber)
        {
            string cmd = $"DISP:WIND{WindowNumber}:TRAC{TraceNumber}:DEL\n;*WAI\n";
            Write(cmd);
            return 0;
        }

        public override int SelectMeasurement(int ChannelNumber, string MeasurementName)
        {
            string cmd = $"CALCulate{ChannelNumber}:PARameter:SELect 'Trc{MeasurementName}'\n;*WAI\n";
            Write(cmd);
            return 0;
        }

        public override int SetDataFormat(int ChannelNumber, string MeasurementName, string DataFormat)
        {
            SelectMeasurement(ChannelNumber, MeasurementName);
            string cmd = $"CALCulate{ChannelNumber}:FORMat {ConvertDataFormatName(DataFormat)}\n;*WAI\n";
            Write(cmd);
            return 0;
        }

        public override int SetScanType(int ChannelNumber, string MeasurementName, string ScanType)
        {
            SelectMeasurement(ChannelNumber, MeasurementName);
            string cmd = $"SENSe{ChannelNumber}:SWEep:TYPE {ConvertScanTypeName(ScanType)}\n;*WAI\n";
            Write(cmd);
            return 0;
        }

        public override int SetStartFrequency(int ChannelNumber, string VNAMeasurementName, long StartFrequency, string Unit = "Hz")
        {
            SelectMeasurement(ChannelNumber, VNAMeasurementName);
            string cmd = $"SENS{ChannelNumber}:FREQ:STAR {StartFrequency}{Unit}\n;*WAI\n";
            Write(cmd);
            return 0;
        }

        public override int SetEndFrequency(int ChannelNumber, string MeasurementName, long EndFrequency, string Unit = "Hz")
        {
            SelectMeasurement(ChannelNumber, MeasurementName);
            string cmd = $"SENS{ChannelNumber}:FREQ:STOP {EndFrequency}{Unit}\n;*WAI\n";
            Write(cmd);
            return 0;
        }

        public override int SetCenterFrequency(int ChannelNumber, string MeasurementName, long CenterFrequency, string Unit = "Hz")
        {
            SelectMeasurement(ChannelNumber, MeasurementName);
            string cmd = $"SENS{ChannelNumber}:FREQ:CENT {CenterFrequency}{Unit}\n;*WAI\n";
            Write(cmd);
            return 0;
        }

        public override int SetFrequencyBandWidth(int ChannelNumber, string MeasurementName, long BandWidth, string Unit = "Hz")
        {
            SelectMeasurement(ChannelNumber, MeasurementName);
            string cmd = $"SENS{ChannelNumber}:FREQ:SPAN {BandWidth}{Unit}\n;*WAI\n";
            Write(cmd);
            return 0;
        }

        public override int SetScanPoints(int ChannelNumber, string MeasurementName, int PointCount)
        {
            SelectMeasurement(ChannelNumber, MeasurementName);
            string cmd = $"SENSe{ChannelNumber}:SWEep:POINts {PointCount}\n;*WAI\n";
            Write(cmd);
            return 0;
        }

        public override int SetOutputPower(int ChannelNumber, double OutputPower_dBm=-20)
        {
            string cmd = $"SOURce{ChannelNumber}:POWer {OutputPower_dBm}dBm\n;*WAI\n";
            Write(cmd);
            return 0;
        }

        public override int SetIntermediateFrequencyBandwidth(int ChannelNumber, long Bandwidth, string Unit = "Hz")
        {
            string cmd = $"SENSe{ChannelNumber}:BANDwidth {Bandwidth}{Unit}\n;*WAI\n";
            Write(cmd);
            return 0;
        }

        public override int SetSmooth(bool Enable, int ChannelNumber, int Points)
        {
            if (Enable)
            {
                string cmd = $"CALC{ChannelNumber}:SMO ON\n;*WAI\n";
                Write(cmd);
                ///这里需要改一下，怎么把点数转换为APERture ，因为zva50没有根据点数设置平滑的指令 
                cmd = $"CALC{ChannelNumber}:SMO:APERture {Points}\n;*WAI\n";  //Aperture 取值范围1-25
                Write(cmd);
                return 0;
            }
            else
            {
                string cmd = $"CALC{ChannelNumber}:SMO OFF\n;*WAI\n";
                Write(cmd);
                return 0;
            }
        }

        public override int SetAverage(bool Enable, int ChannelNumber, int Count)
        {
            if (Enable)
            {
                string cmd = $"SENS{ChannelNumber}:AVER ON\n;*WAI\n";
                Write(cmd);
                cmd = $"SENS{ChannelNumber}:AVER:COUN {Count}\n;*WAI\n";
                Write(cmd);
                return 0;
            }
            else
            {
                string cmd = $"SENS{ChannelNumber}:AVER OFF\n;*WAI\n";
                Write(cmd);
                return 0;
            }
        }

        public override int SetYScaleAuto(int WindowNumber, int TraceNumber)
        {
            //string cmd = $"DISP:WIND{WindowNumber}:TRAC{TraceNumber}:Y:AUTO ONCE,'Trc{TraceNumber}'\n;*WAI\n";
            ////string cmd = $"DISP:WIND{WindowNumber}:Y:AUTO\n;*WAI\n";
            //Write(cmd);
            return 0;
        }

        public override int SetTriggerSource(TriggerSource TriggerSource)
        {
            string temp = "";
            switch (TriggerSource)
            {
                case TriggerSource.外部:
                    temp = "EXTernal";
                    break;
                case TriggerSource.内部:
                    temp = "IMMediate";
                    break;
                case TriggerSource.手动:
                    temp = "IMMediate";
                    break;
                default:
                    temp = "MANual";
                    break;
            }

            string cmd = $"TRIG:SOUR {temp}\n;*WAI\n";
            Write(cmd);
            return 0;
        }

        public override int SetTriggerScope(TriggerScope TriggerScope)
        {
            string temp = "";
            switch (TriggerScope)
            {
                case TriggerScope.ALL:
                    temp = "ALL";
                    break;
                case TriggerScope.ONE:
                    temp = "SINGle";
                    break;
                default:
                    temp = "ALL";
                    break;
            }

            string cmd = $"INITiate{1}:SCOPe {temp}\n;*WAI\n";
            Write(cmd);
            return 0;
        }

        public override int SetTriggerInput(string Interface, TriggerSignalType TriggerSignalType, double Width, double Delay_ms = 0)
        {
            //本仪器只有一个接口，不必设置
            string temp = "";
            switch (TriggerSignalType)
            {
                case TriggerSignalType.高电平:
                    temp = "HIGH";
                    break;
                case TriggerSignalType.低电平:
                    temp = "LOW";
                    break;
                default:
                    temp = "HIGH";
                    break;
            }
            string cmd = $"TRIG:LEV {temp}\n;*WAI\n";
            Write(cmd);


            //输入脉冲宽度不处理
            //延时触发不处理
            //cmd = $"TRIGger:SEQuence:EXTernal:DELay {Delay_ms/1000}\n;*WAI\n";
            //设置驻留时间

            return 0;
        }

        public override int SetTriggerOut(bool Enable, string Interface, int ChannelNumber, string time,TriggerSignalType TriggerSignalType, double Width)
        {
            //Interface只有一个不用设置
            //通道不处理


            //string temp = "";
            //switch (time)
            //{
            //    case "生成测量点之后":
            //        temp = "AFTer";
            //        break;
            //    case "生成测量点之前":
            //        temp = "BEFore";
            //        break;
            //    default:
            //        temp = "AFTer";
            //        break;
            //}

            //string cmd = $"TRIG:OUTP:POS {temp}\n;*WAI\n";
            //Write(cmd);
            //Wait(100);
            //switch (TriggerSignalType)
            //{
            //    case TriggerSignalType.高电平:
            //        temp = "POSitive";
            //        break;
            //    case TriggerSignalType.低电平:
            //        temp = "NEGative";
            //        break;
            //    default:
            //        temp = "POSitive";
            //        break;
            //}

            //cmd = $"TRIG:OUTP:POL {temp}\n;*WAI\n";
            //Write(cmd);
            //Wait(100);


            ////width未找到指令，暂不实现
            ////？？
            ////？？
            ////？？

            ////设置驻留时间
            //cmd = $"SENS:SWE:GEN STEP\n;*WAI\n";
            //Write(cmd);
            //Wait(100);
            //cmd = $"SENS:SWE:DWEL:AUTO OFF\n;*WAI\n";
            //Write(cmd);
            //Wait(100);
            //cmd = $"SENS:SWE:DWEL:SDELay {double.Parse(DWell) / 1000000}\n;*WAI\n";
            //Write(cmd);


            //if (Enable)
            //{
            //    cmd = $"TRIG:OUTP:STAT ON\n;*WAI\n";
            //    Write(cmd);
            //}
            //else
            //{
            //    cmd = $"TRIG:OUTP:STAT OFF\n;*WAI\n";
            //    Write(cmd);
            //}
            return 0;
        }

        public override int SetSweepMode(SweepMode SweepMode = SweepMode.连续)
        {
            //string temp = "";
            //switch (SweepMode)
            //{
            //    case SweepMode.连续:
            //        temp = "CONTinuous";
            //        break;
            //    case SweepMode.保持:
            //        temp = "HOLD";
            //        break;
            //    default:
            //        temp = "CONTinuous";
            //        break;
            //}

            //string cmd = $"SENS:SWE:MODE {temp}\n;*WAI\n";
            //Write(cmd);
            return 0;
        }

        public override int SetSweepScope(SweepScope SweepScope = SweepScope.通道)
        {
            //触发一次测量一个点还是通道上的所有测量都测一遍 ON一次一个 OFF一次所有点
            //string temp = "";
            //switch (SweepScope)
            //{
            //    case SweepScope.通道:
            //        temp = "OFF";
            //        break;
            //    case SweepScope.点:
            //        temp = "ON";
            //        break;
            //    default:
            //        temp = "OFF";
            //        break;
            //}

            //string cmd = $"SENS:SWE:TRIG:POIN {temp}\n;*WAI\n";
            ////string cmd = $"TRIGger:SEQuence:POINt {temp}\n";
            //Write(cmd);
            return 0;
        }






        public void Init(string MeasureType)
        {
            throw new NotImplementedException();
        }

        public override string ConvertDataFormatName(string DataFormatName)
        {
            switch (DataFormatName)
            {
                case "线性幅度":
                    return "MLINear";
                case "对数幅度":
                    return "MLOGarithmic";
                case "相位":
                    return "PHASe";
                case "虚部":
                    return "IMAGinary";
                case "实部":
                    return "REAL";
                case "极坐标":
                    return "POLar";
                case "史密斯圆":
                    return "SMITh";
                case "驻波比":
                    return "SWR";
                case "群延时":
                    return "GDELay";
                default:
                    throw new Exception("未识别的数据类型!");
            }
        }

        private string ConvertScanTypeName(string ScanTypeName)
        {
            switch (ScanTypeName)
            {
                case "线性频率":
                    return "LINear";
                case "对数频率":
                    return "LOGarithmic";
                case "功率扫描":
                    return "POWer";
                case "点频":
                    return "CW";
                case "段扫描":
                    return "SEGMent";
                default:
                    throw new Exception("未识别的扫描类型!");
            }
        }

        public override int Wait(int PollingTime_ms)
        {
            //等待测量完成，两种方式
            //*OPC 测量完成后自动报告，未完成时，矢网不执行其他指令
            //*OPC? 查询是否完成，不会阻塞矢网，完成后读到1，否则0 ，可以用轮询的方式
            //这里采用轮询的方式，因为visa读取数据时，会有超时报错，不能等太长时间
            bool Completed = false;
            byte[] Data = new byte[8];
            while (!Completed)
            {
                Write("*OPC?\n");
                Array.Clear(Data, 0, 8);
                Read(Data);
                if (System.Text.Encoding.Default.GetString(Data).Replace("\0", "").Replace("\n","") == "1")
                {
                    Completed = true;
                    break;
                }
                Thread.Sleep(PollingTime_ms);//等待下一次轮询
            }
            return 0;
        }

        public override int GetError(ref string ErrorInfo)
        {
            string cmd = $"SYST:ERR:ALL?\n";
            Write(cmd);
            //读取仪器状态确认
            byte[] Temp = new byte[12800];

            Read(Temp);
            string Msg = System.Text.Encoding.Default.GetString(Temp).Replace("\0", "").Replace("\n", "");
            if (!Msg.ToUpper().Contains("No error".ToUpper()))
            {
                //cmd = $"SYST:ERR?\n;*WAI\n";
                //Write(cmd);
                //Thread.Sleep(100);
                ////读取仪器状态确认
                //Temp = new byte[128];
                //Read(Temp);
                //ErrorInfo = System.Text.Encoding.GetEncoding("GBK").GetString(Temp).Replace("\0", "").Replace("\n", "");
                //return 1;


                ErrorInfo = Msg;
                return 1;
            }
            ErrorInfo = "";
            return 0;
        }

        //public override string GetVNAMeasureName(string DataFormat, ObservableCollection<string> DataFormatList)
        //{
        //    string Name = "";
        //    if (DataFormatList == null)
        //    {
        //        throw new ArgumentNullException(nameof(DataFormatList));
        //    }
        //    if (string.IsNullOrEmpty(DataFormat))
        //    {
        //        throw new ArgumentNullException(nameof(DataFormat));
        //    }

        //    int Index = DataFormatList.IndexOf(DataFormat);
        //    if (Index == -1)
        //    {
        //        throw new Exception($"创建轨迹名称失败，不支持的数据格式{DataFormat}!");
        //    }
        //    else
        //    {
        //        return "Trc"+Index.ToString();
        //    }
        //}
    }
}
